package com.maple.common.widget

import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.RectF
import android.util.AttributeSet
import android.view.View
import androidx.annotation.ColorInt
import androidx.core.content.withStyledAttributes
import com.maple.common.R
import com.maple.common.log.LogUtil
import com.maple.common.ext.dpToPx

/**
 * 信号柱
 * Tip:signal pole(night mode)
 * levelColor signal pole default color white
 * primaryColor signal pole NONE color #242424
 * Create by SeVen on 2023/11/14 10:40
 */
class SignalView @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {

    /**
     * signal pole quantity,default value 5
     */
    private var signalMaximum: Int = 5

    /**
     * signal level ,default value 0
     */
    private var signalLevel: Int = 0

    /**
     * signal pole color ,default color white
     */
    @ColorInt
    private var levelColor: Int = Color.WHITE

    /**
     * signal pole primary color ,default color black(242424)
     */
    @ColorInt
    private var primaryColor: Int = Color.parseColor("#242424")

    /**
     * signal pole spacing ,default 2
     */
    private var spacing: Int = 2

    /**
     * signal pole unit Width default value 5
     */
    private var unitWidth: Int = 5

    /**
     * signal connect state ,default value true
     */
    private var connected: Boolean = true


    /**
     * signal pole shadow color, default color: gray
     */
    @ColorInt
    private var shadowColor: Int = Color.GRAY

    /**
     * signal pole shadow switch, true: open,false: close, default state: false(close)
     */
    private var shadowOpen: Boolean = false

    /**
     * signal pole paint
     */
    private var mPaint: Paint? = null

    /**
     * paint rect height, init value 0
     */
    private var mRectHeight: Int = 0

    /**
     * paint rect width,init value 0
     */
    private var mRectWidth: Int = 0

    init {
        initAttrs(context, attrs)
        mPaint = initPaint()
    }

    private fun initPaint(): Paint {
        return Paint().also {
            it.isAntiAlias = true
            shadowSwitch(it)
        }
    }

    private fun shadowSwitch(paint: Paint) {
        if (shadowOpen) {
            val shadowWith = (unitWidth * 0.2).toFloat()
            paint.setShadowLayer(5F, shadowWith, shadowWith, shadowColor)
            setLayerType(LAYER_TYPE_SOFTWARE, null)
        }
    }

    /**
     * init attrs
     */
    private fun initAttrs(context: Context, attrs: AttributeSet?) {
        context.withStyledAttributes(attrs, R.styleable.SignalView) {
            also {
                it.apply {
                    signalMaximum = getInt(R.styleable.SignalView_sv_signalMaximum, signalMaximum)
                    signalLevel = getInt(R.styleable.SignalView_sv_signalLevel, signalLevel)
                    primaryColor = getColor(R.styleable.SignalView_sv_primaryColor, primaryColor)
                    levelColor = getColor(R.styleable.SignalView_sv_levelColor, levelColor)
                    spacing = getInt(R.styleable.SignalView_sv_spacing, spacing)
                    unitWidth = getInt(R.styleable.SignalView_sv_unitWidth, unitWidth)
                    connected = getBoolean(R.styleable.SignalView_sv_connected, connected)
                    shadowColor = getColor(R.styleable.SignalView_sv_shadowColor, shadowColor)
                    shadowOpen = getBoolean(R.styleable.SignalView_sv_shadowOpen, shadowOpen)
                }
            }
        }
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        LogUtil.d("onDraw#$mRectWidth", tag = "SignalView")
        mPaint?.also {
            it.style = Paint.Style.FILL
            canvasSignalPole(it, canvas)
            canvasSignalConnectState(it, canvas)
        }
    }

    /**
     * canvas signal pole
     */
    private fun canvasSignalPole(paint: Paint, canvas: Canvas?) {
        val rectWidthFactor = 0.5f
        val rectStartYFactor = 0.1f
        val rectStopYFactor = 0.8f
        val rectStopY = height * rectStopYFactor

        //fun getRectX(index: Int) = mRectWidth * (index + rectWidthFactor) + spacing
        fun getRectX(index: Int) =
            (mRectWidth - unitWidth + spacing) * (index + rectWidthFactor)

        //fun getRectStartY(index: Int) = mRectHeight * (signalMaximum - index) * rectStartYFactor
        fun getRectStartY(index: Int) = mRectHeight * (signalMaximum - index) * rectStartYFactor


        //canvas signal pole, display different colors based on strength
        (0 until signalMaximum).forEach { index ->
            paint.color = if (index < signalLevel) levelColor else primaryColor
            val rectStartX = getRectX(index)
            val rectStopX = rectStartX + unitWidth

            //canvas signal pole
            val rect = RectF(rectStartX, getRectStartY(index), rectStopX, rectStopY)
            canvas?.drawRoundRect(rect, 5f, 5f, paint)
        }
    }

    private fun canvasSignalConnectState(paint: Paint, canvas: Canvas?) {
        if (connected) return
        paint.color = primaryColor
        paint.strokeWidth = unitWidth.toFloat()
        paint.strokeCap = Paint.Cap.ROUND
        val sum = 1f
        val xDifference = 0.2f
        val yDifference = 0.1f
        canvas?.drawLine(
            width * xDifference,
            height * yDifference,
            width * (sum - xDifference),
            height * (sum - yDifference),
            paint
        )
    }

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        initSize()
    }

    private fun initSize() {
        mRectHeight = height
        mRectWidth = width / signalMaximum
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        setMeasuredDimension(measuredWidth(widthMeasureSpec), measuredHeight(heightMeasureSpec))
    }

    private fun getMeasureSpaceInfo(measureSpec: Int): Pair<Int, Int> {
        //测量模式，从xml获取
        val specMode = MeasureSpec.getMode(measureSpec)
        //测量大小，从xml获取
        val specSize = MeasureSpec.getSize(measureSpec)
        return Pair(specMode, specSize)
    }

    private fun measureSize(specMode: Int, specSize: Int, defaultSpecSize: Int): Int {
        return if (specMode == MeasureSpec.EXACTLY) specSize else {
            if (specMode == MeasureSpec.AT_MOST) {
                defaultSpecSize.coerceAtMost(specSize)
            } else defaultSpecSize
        }
    }

    private fun measuredWidth(widthMeasureSpec: Int): Int {
        val (specMode, specSize) = getMeasureSpaceInfo(widthMeasureSpec)
        //计算每一条signal pole signalMaximum * unitWidth + (signalMaximum-1)  * space
        val w = signalMaximum * unitWidth - spacing
        val defaultSpecSize = dpToPx(context,w)
        return measureSize(specMode, specSize, defaultSpecSize)
    }

    private fun measuredHeight(heightMeasureSpec: Int): Int {
        val (specMode, specSize) = getMeasureSpaceInfo(heightMeasureSpec)
        val defaultSpecSize = dpToPx(context,18)
        val height = measureSize(specMode, specSize, defaultSpecSize)
        mRectHeight = height
        return height
    }

    /**
     * set signal  pole
     */
    fun setSignalLevel(level: Int) {
        LogUtil.d("setSignalLevel#$level", tag = "SignalView")
        if (level > signalMaximum) {
            throw Exception("setSignalLevel method value error,can not exceed settings signalMaximum!")
        }
        if (signalLevel != level) {
            signalLevel = level
            initSize()
            this.invalidate()
        }
    }

    /**
     * set connected
     */
    fun setConnected(connected: Boolean) {
        if (this.connected != connected) {
            this.connected = connected
            initSize()
            this.invalidate()
        }
    }

    private fun build(builder: Builder) {
        signalMaximum = builder.signalMaximum ?: signalMaximum
        signalLevel = builder.signalLevel ?: signalLevel
        primaryColor = builder.primaryColor ?: primaryColor
        levelColor = builder.levelColor ?: levelColor
        spacing = builder.spacing ?: spacing
        unitWidth = builder.unitWidth ?: unitWidth
        connected = builder.connected ?: connected
        shadowColor = builder.shadowColor ?: shadowColor
        shadowOpen = builder.shadowOpen ?: shadowOpen
        initSize()
        this.invalidate()
    }

    inner class Builder {
        /**
         * signal pole quantity, default is null
         */
        var signalMaximum: Int? = null
            private set

        /**
         * signal level, default is null
         */
        var signalLevel: Int? = null
            private set

        /**
         * signal pole primary color, default value: null
         */
        @ColorInt
        var primaryColor: Int? = null
            private set

        /**
         * signal pole level color, default color: null
         */
        @ColorInt
        var levelColor: Int? = null
            private set

        /**
         * signal pole spacing, default value: null
         */
        var spacing: Int? = null
            private set

        /**
         * signal pole unit width, default value: null
         */
        var unitWidth: Int? = null
            private set

        /**
         * signal connect state, true: connected; false: unconnected, default value: null
         */
        var connected: Boolean? = null
            private set

        /**
         * signal pole shadow color, default color: null
         */
        @ColorInt
        var shadowColor: Int? = null
            private set

        /**
         * signal pole shadow switch, true: open,false: close, default state: false(close)
         */
        var shadowOpen: Boolean? = null
            private set


        fun setSignalMaximum(signalMaximum: Int) = apply { this.signalMaximum = signalMaximum }

        fun setSignalLevel(signalLevel: Int) = apply { this.signalLevel = signalLevel }

        fun setPrimaryColor(@ColorInt primaryColor: Int) =
            apply { this.primaryColor = primaryColor }

        fun setLevelColor(@ColorInt levelColor: Int) = apply { this.levelColor = levelColor }

        fun setSpacing(spacing: Int) = apply { this.spacing = spacing }

        fun setUnitWidth(unitWidth: Int) = apply { this.unitWidth = unitWidth }

        fun setConnected(connected: Boolean) = apply { this.connected = connected }

        fun setShadowColor(@ColorInt shadowColor: Int) = apply { this.shadowColor = shadowColor }

        fun setShadowOpen(shadowOpen: Boolean) = apply { this.shadowOpen = shadowOpen }

        fun build() {
            this@SignalView.build(this)
        }
    }
}